/**
 * @brief 与 MCU 时钟 / RTC 相关的实现
 * @author 张勇 / 2020-05-13
 */

#include <stdint.h>
#include <string.h>
#include <system_stm32f10x.h>
#include <stm32f10x_pwr.h>
#include <stm32f10x_rcc.h>
#include <stm32f10x_rtc.h>

#include "share/cccompat.h"
#include "share/utils.h"
#include "share/calendar.h"
#include "share/unios.h"
#include "mcu.h"
#include "_mcu.h"

#include "share/log.h"
#define LOGLEVEL LOG_DEBUG
#define LOGTAG   "MCU"
#include "share/log4app.h"

// 外设时钟实际值
static uint32_t _ahb_freq  = 16800000;
static uint32_t _apb1_freq = 42000000;
static uint32_t _apb2_freq = 84000000;
uint32_t mcu_clock_get_ahb_freq()  { return _ahb_freq;  }
uint32_t mcu_clock_get_apb1_freq() { return _apb1_freq; }
uint32_t mcu_clock_get_apb2_freq() { return _apb2_freq; }

// 系统滴答定时器: 使用SysTick
/// 最大计数值
#define MCU_SYSTEM_TICK_MAX MAXU32

/// 看门狗复位时间/最小喂狗时间(ms)
#define WATCHDOG_FEED_MAX_TIME  10000

/// SystemCoreClock 变量声明见 system_stm32f10x.h，定义见 system_stm32f10x.c
uint32_t mcu_clock_get_sysfreq(void) {
	return SystemCoreClock;
}

/// @brief ms 计数器
volatile uint32_t _tick_count_ms = 0;

/// @brief 系统滴答时钟中断服务函数
_RAMCODE_ void SysTick_Handler(void) {
	_tick_count_ms++;
}

/// 初始化系统滴答定时器
static std_err_t _systick_timer_init(void) {
	// 设置重装载计数值，以使 SysTick 定时器 1ms 产生一次中断
	SysTick->LOAD = MCU_CLOCK_FREQ/8000UL - 1UL;
	// Load the SysTick Counter Value
	SysTick->VAL  = 0UL;
	// 时钟源为 HCLK/8(MCU主时钟源/8)
	SysTick->CTRL = 0;
	// Enable SysTick IRQ and SysTick Timer
	SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
	return STD_ERR_OK;
}

uint32_t mcu_clock_get_systick(void) {
	// 获取 SysTick 定时器计数值
	uint32_t systick = SysTick->VAL;
	// 把24位向下计数值转换为正向计数值
	systick = 0x1000000 - systick;
	// 返回时加上 ms 换算出来的部分
	return (_tick_count_ms * (MCU_CLOCK_FREQ/8000UL)) + systick;
}


// 毫秒定时器(使用 SysTick)
uint64_t mcu_clock_ms_now(void) {
	return _tick_count_ms;
}

void mcu_clock_ms_delay(uint32_t num) {
	uint32_t curr = _tick_count_ms;
	while(unios_ms_elapsed(curr) < num);
}

// 微秒定时器: 使用SysTick
_RAMCODE_ uint64_t mcu_clock_us_now(void) {
	// 获取 SysTick 定时器计数值
	uint32_t us = SysTick->VAL;
	// 把24位向下计数值转换为正向计数值
	us = 0x1000000 - us;
	// 换算成 us
	us /= MCU_CLOCK_FREQ/8000000UL;
	// 返回时加上 ms 换算出来的部分
	return (_tick_count_ms * 1000) + us;
}

_RAMCODE_ void mcu_clock_us_delay(uint32_t num) {
	uint32_t curr = mcu_clock_us_now();
	while(unios_us_elapsed(curr) < num);
}

/// 初始化 RTC 时钟, 同时检测时钟是否工作正常, BKP->DR1用于保存是否第一次配置的设置
/// @param weekday 0: 星期天, 1~6: 星期一 ~ 星期六
/// @param hour 0~23
static std_err_t _rtc_init() {
    std_err_t err;
	// 使能 PWR 管理时钟
	RCC_PeriphClockCmd_PWR(ENABLE);
	// 使能后备区时钟
	RCC_PeriphClockCmd_BKP(ENABLE);
	// 使能后备寄存器访问
	PWR_BackupAccessCmd(ENABLE);
	
	// 如果已经设置 RTC 时间，就不要再次设置了
	mcu_rtc_bkp_t *bkp = mcu_rtc_bkp_read();
	if(bkp->rtc_setup) {
		// RTC 已经初始过，系统继续计时
		// 等待 RTC 寄存器同步
		RTC_WaitForSynchro();
		// 使能 RTC 秒中断，并等待对RTC寄存器的写操作完成
		RTC_ITConfig(RTC_IT_SEC, ENABLE); RTC_WaitForLastTask();
		logd("RTC running: %s\r\n", unios_rtc_local_string());
		return STD_ERR_OK;
	}
	
	// 第一次配置时钟
    uint32_t clksrc;
	uint32_t prescaler = 0;
	
    // 设置 RTC 时钟源
	if(MCU_RTC_CLKSRC_LSE) {
		// 使用外设低速晶振LSE(32768 Hz)
        clksrc = RCC_RTCCLKSource_LSE;
		logd("RTC first init @LSE ...\r\n");
		// 启动晶振，并等待晶振稳定
		RCC_LSEConfig(RCC_LSE_ON);
		uint32_t timeout = 1000000UL;
		unios_wait_us_until(timeout, timeout, RCC_GetFlagStatus(RCC_FLAG_LSERDY));
		// 初始化时钟失败，可能是晶振有问题
		if(timeout == 0) { return STD_ERR_NOT_SUPPORTED; }
		// RTC预分频的值(1s中断一次)
		prescaler = 32768;
	} else if(MCU_RTC_CLKSRC_HSE_Div128) {
		// 使用外部高速时钟HSE/128(8MHz/128=62.5K)
        clksrc = RCC_RTCCLKSource_HSE_Div128;
		logw("RTC first init @HSE/128 ...\r\n");
		// RTC预分频的值(1s中断一次)
		prescaler = HSE_VALUE/128;
	} else {
		// 使用内部低速时钟LSI(40KHz)
        clksrc = RCC_RTCCLKSource_LSI;
		logw("RTC first init @LSI ...\r\n");
		// 启动晶振，并等待晶振稳定
		RCC_LSICmd(ENABLE);
		uint32_t timeout = 1000000UL;
		unios_wait_us_until(timeout, timeout, RCC_GetFlagStatus(RCC_FLAG_LSIRDY));
		// 初始化时钟失败，可能是晶振有问题
		if(timeout == 0) { return STD_ERR_NOT_SUPPORTED; }
		// RTC预分频的值(1s中断一次)
		prescaler = 40000;
	}

	// 复位备份区域
	BKP_DeInit();
	
	// 设置 RTC 时钟(RTCCLK)
	RCC_RTCCLKConfig(clksrc);
	// 使能RTC时钟，并等待对RTC寄存器的写操作完成
	RCC_RTCCLKCmd(ENABLE); RTC_WaitForLastTask();
	// 等待 RTC 寄存器同步
	RTC_WaitForSynchro();

	// 进入 RTC 配置模式
	RTC_EnterConfigMode();
	// 设置RTC预分频的值(1s中断一次)，并等待对RTC寄存器的写操作完成
	RTC_SetPrescaler(prescaler-1); RTC_WaitForLastTask();
	// 退出 RTC 配置模式
	RTC_ExitConfigMode();
	
	// 使能 RTC 秒中断，并等待对RTC寄存器的写操作完成
	RTC_ITConfig(RTC_IT_SEC, ENABLE); RTC_WaitForLastTask();
	
    // 写入 RTC 初始化标志，表示已经初始化过
    bkp->rtc_setup = true;
    err = mcu_rtc_bkp_write(bkp); if(err) return err;
	
	return STD_ERR_OK;
}

uint32_t mcu_clock_get_rtcsec(void) {
	return RTC_GetCounter();
}

std_err_t mcu_clock_set_rtcsec(uint32_t sec) {
	// 设置 RTC 计数器的值，并等待对RTC寄存器的写操作完成
	RTC_SetCounter(sec); RTC_WaitForLastTask();
	return STD_ERR_OK;
}

std_err_t mcu_clock_rtc_set(int year, int month, int day, int hour, int minute, int second) {
    if(year > 9999 || month > 12 || day > 31 || hour > 23 || minute > 60 || second > 60) return STD_ERR_INVALID_ARG;
    unios_datetime_t dt = {.date = {year, month, day}, .time = {hour, minute, second}};
    return mcu_clock_set_rtcsec(dateTime2Seconds(&dt));
}

std_err_t mcu_clock_rtc_get(int *year, int *month, int *day, int *hour, int *minute, int *second, int *weekday) {
    unios_datetime_t dt = seconds2DateTime(mcu_clock_get_rtcsec());
    if(year)    { *year    = dt.date.year   ; }
    if(month)   { *month   = dt.date.month  ; }
    if(day)     { *day     = dt.date.day    ; }
    if(weekday) { *weekday = dt.date.weekday; }
    if(hour)    { *hour    = dt.time.hour   ; }
    if(minute)  { *minute  = dt.time.minute ; }
    if(second)  { *second  = dt.time.second ; }
    return STD_ERR_OK;
}

/// 秒中断回调函数
static mcu_isr_t _rtc_second_handler = NULL;
static void* _rtc_second_handler_cookie = NULL;
/// 闹钟中断回调函数
static mcu_isr_t _rtc_alarm_handler = NULL;
static void* _rtc_alarm_handler_cookie = NULL;

void mcu_clock_set_rtcsec_handler(mcu_isr_t handler, void* cookie) {
	_rtc_second_handler = handler;
	_rtc_second_handler_cookie = cookie;
}

std_err_t mcu_clock_set_rtc_alarm(uint32_t timestamp, mcu_isr_t handler, void* cookie) {
	if(!timestamp || !handler || !cookie) { return STD_ERR_INVALID_ARG; }
	// 设置时钟，并等待对RTC寄存器的写操作完成
	RTC_SetAlarm(timestamp); RTC_WaitForLastTask();
	// 使能 RTC 秒中断，并等待对RTC寄存器的写操作完成
	RTC_ITConfig(RTC_IT_SEC, ENABLE); RTC_WaitForLastTask();
	// 保存回调函数
	_rtc_alarm_handler = handler;
	_rtc_alarm_handler_cookie = cookie;
	return STD_ERR_OK;
}

/// RTC时钟中断(每秒触发一次)
_RAMCODE_ void RTC_IRQHandler(void) {
	// 秒钟中断
	if(RTC_GetITStatus(RTC_IT_SEC) != RESET) {
		// 秒钟中断回调
		if(_rtc_second_handler) { _rtc_second_handler(_rtc_second_handler_cookie); }
 	}
	
	// 闹钟中断
	if(RTC_GetITStatus(RTC_IT_ALR) != RESET) {
		logd("Alarm @%s\n", unios_rtc_local_string());
		// 闹钟中断回调
		if(_rtc_alarm_handler) { _rtc_alarm_handler(_rtc_alarm_handler_cookie); }
  	} 				  								
	// 清闹钟中断
	RTC_ClearITPendingBit(RTC_IT_SEC | RTC_IT_ALR | RTC_IT_OW);
	RTC_WaitForLastTask();
}

// RTC 后备区寄存器值缓冲区
static mcu_rtc_bkp_t _bkp;
static uint16_t* const _bkpregs = (uint16_t*)&_bkp;

mcu_rtc_bkp_t* mcu_rtc_bkp_read() {
    for(int i=0; i < sizeof(_bkp) / 2; i++) {
        _bkpregs[i] = BKP_ReadBackupRegister(i + 1); // 后备寄存器地址为: 1..42
    }
    return &_bkp;
}

std_err_t mcu_rtc_bkp_write(mcu_rtc_bkp_t *bkp) {
    if(!bkp) return STD_ERR_INVALID_ARG;
    for(int i=0; i < sizeof(_bkp) / 2; i++) {
        BKP_WriteBackupRegister(i + 1, _bkpregs[i]); // 后备寄存器地址为: 1..42
    }
    return STD_ERR_OK;
}

std_err_t mcu_clock_init() {
	static std_err_t err;
	err = _systick_timer_init();
	retonerr(err, "_systick_timer_init(): %s\r\n", std_err_comment(err));
	
    // 初始化 RTC
	err = _rtc_init();
    retonerr(err, "_rtc_init(): %s\r\n", std_err_comment(err));
	
	return STD_ERR_OK;
}
